<!--
 * @FileDescription:
 * @Author: 茆生涛
 * @Date: 2021-07-26 17:22:19
 * @LastEditors: mst
 * @LastEditTime: 2021-11-01 11:10:33
-->
<template>
  <div class="random">
    <div id="container"></div>
    <div id="menu">
      <button id="table">表格</button>
      <button id="sphere">圆球</button>
      <button id="helix">螺旋</button>
      <button id="grid">网格</button>
    </div>
  </div>
</template>
<script>
import * as THREE from '@/utils/three.module.js'
import { TWEEN } from '@/utils/tween.module.min.js'
import { TrackballControls } from '@/utils/TrackballControls.js'
import { CSS3DRenderer, CSS3DObject } from '@/utils/CSS3DRenderer.js'
import { MENUS } from '@/config/const'
export default {
  data () {
    return {
      camera: undefined,
      scene: undefined,
      renderer: undefined,
      objects: [],
      targets: { table: [], sphere: [], helix: [], grid: [] }
    }
  },
  mounted () {
    this.init()
    this.animate()
  },
  methods: {
    init () {
      this.camera = new THREE.PerspectiveCamera(
        40, // 视线和水平的夹角
        window.innerWidth / window.innerHeight, // 长宽比
        1, // 近平面
        10000 // 远平面
      )
      this.camera.position.z = 5000

      this.scene = new THREE.Scene()

      // table

      for (let i = 0; i < MENUS.length; i += 5) {
        const element = document.createElement('div')
        element.className = 'element'
        element.style.backgroundColor =
          'rgba(0,127,127,' + (Math.random() * 0.5 + 0.25) + ')'

        const number = document.createElement('div')
        number.className = 'number'
        number.textContent = i / 5 + 1
        element.appendChild(number)

        const symbol = document.createElement('div')
        symbol.className = 'symbol'
        symbol.textContent = MENUS[i]
        element.appendChild(symbol)

        const details = document.createElement('div')
        details.className = 'details'
        details.innerHTML = MENUS[i + 1] + '<br>' + MENUS[i + 2]
        element.appendChild(details)
        const tableRandomNum = 4000
        const offset = 2000
        const objectCSS = new CSS3DObject(element)
        objectCSS.position.x = Math.random() * tableRandomNum - offset
        objectCSS.position.y = Math.random() * tableRandomNum - offset
        objectCSS.position.z = Math.random() * tableRandomNum - offset
        this.scene.add(objectCSS)

        this.objects.push(objectCSS)

        //

        const object = new THREE.Object3D()
        object.position.x = MENUS[i + 3] * 140 - 1330
        object.position.y = -(MENUS[i + 4] * 180) + 990

        this.targets.table.push(object)
      }

      // sphere

      const vector = new THREE.Vector3()

      for (let i = 0, l = this.objects.length; i < l; i++) {
        const phi = Math.acos(-1 + (2 * i) / l)
        const theta = Math.sqrt(l * Math.PI) * phi

        const object = new THREE.Object3D()

        object.position.setFromSphericalCoords(800, phi, theta)

        vector.copy(object.position).multiplyScalar(2)

        object.lookAt(vector)

        this.targets.sphere.push(object)
      }

      // helix

      for (let i = 0, l = this.objects.length; i < l; i++) {
        const theta = i * 0.175 + Math.PI
        const y = -(i * 8) + 450

        const object = new THREE.Object3D()

        object.position.setFromCylindricalCoords(900, theta, y)

        vector.x = object.position.x * 2
        vector.y = object.position.y
        vector.z = object.position.z * 2

        object.lookAt(vector)

        this.targets.helix.push(object)
      }

      // grid

      for (let i = 0; i < this.objects.length; i++) {
        const object = new THREE.Object3D()

        object.position.x = (i % 5) * 400 - 800
        object.position.y = -(Math.floor(i / 5) % 5) * 400 + 800
        object.position.z = Math.floor(i / 25) * 1000 - 2000

        this.targets.grid.push(object)
      }

      //

      this.renderer = new CSS3DRenderer()
      this.renderer.setSize(window.innerWidth, window.innerHeight)
      document
        .getElementById('container')
        .appendChild(this.renderer.domElement)

      //

      this.controls = new TrackballControls(
        this.camera,
        this.renderer.domElement
      )
      this.controls.minDistance = 500
      this.controls.maxDistance = 6000
      this.controls.addEventListener('change', this.render)

      const buttonTable = document.getElementById('table')
      buttonTable.addEventListener('click', () => {
        this.transform(this.targets.table, 2000)
      })

      const buttonSphere = document.getElementById('sphere')
      buttonSphere.addEventListener('click', () => {
        this.transform(this.targets.sphere, 2000)
      })

      const buttonHelix = document.getElementById('helix')
      buttonHelix.addEventListener('click', () => {
        this.transform(this.targets.helix, 2000)
      })

      const buttonGrid = document.getElementById('grid')
      buttonGrid.addEventListener('click', () => {
        this.transform(this.targets.grid, 2000)
      })

      this.transform(this.targets.table, 2000)

      //

      window.addEventListener('resize', this.onWindowResize)
    },
    transform (targets, duration) {
      TWEEN.removeAll()

      for (let i = 0; i < this.objects.length; i++) {
        const object = this.objects[i]
        const target = targets[i]

        new TWEEN.Tween(object.position)
          .to(
            {
              x: target.position.x,
              y: target.position.y,
              z: target.position.z
            },
            Math.random() * duration + duration
          )
          .easing(TWEEN.Easing.Exponential.InOut)
          .start()

        new TWEEN.Tween(object.rotation)
          .to(
            {
              x: target.rotation.x,
              y: target.rotation.y,
              z: target.rotation.z
            },
            Math.random() * duration + duration
          )
          .easing(TWEEN.Easing.Exponential.InOut)
          .start()
      }

      new TWEEN.Tween(this)
        .to({}, duration * 2)
        .onUpdate(this.render)
        .start()
    },
    onWindowResize () {
      this.camera.aspect = window.innerWidth / window.innerHeight
      this.camera.updateProjectionMatrix()

      this.renderer.setSize(window.innerWidth, window.innerHeight)

      this.render()
    },

    animate () {
      requestAnimationFrame(this.animate)

      TWEEN.update()

      this.controls.update()
    },

    render () {
      this.renderer.render(this.scene, this.camera)
    }
  }
}
</script>
<style lang="scss">
.random{
  background: #000000;
}
a {
  color: #8ff;
}

#menu {
  position: absolute;
  bottom: 66px;
  width: 100%;
  text-align: center;
}

.element {
  width: 120px;
  height: 160px;
  box-shadow: 0px 0px 12px rgba(0, 255, 255, 0.5);
  border: 1px solid rgba(127, 255, 255, 0.25);
  font-family: Helvetica, sans-serif;
  text-align: center;
  line-height: normal;
  cursor: default;
}

.element:hover {
  box-shadow: 0px 0px 12px rgba(0, 255, 255, 0.75);
  border: 1px solid rgba(127, 255, 255, 0.75);
}

.element .number {
  position: absolute;
  top: 20px;
  right: 20px;
  font-size: 12px;
  color: rgba(127, 255, 255, 0.75);
}

.element .symbol {
  position: absolute;
  top: 40px;
  left: 0px;
  right: 0px;
  font-size: 60px;
  font-weight: bold;
  color: rgba(255, 255, 255, 0.75);
  text-shadow: 0 0 10px rgba(0, 255, 255, 0.95);
}

.element .details {
  position: absolute;
  bottom: 15px;
  left: 0px;
  right: 0px;
  font-size: 12px;
  color: rgba(127, 255, 255, 0.75);
}

button {
  color: rgba(127, 255, 255, 0.75);
  background: transparent;
  outline: 1px solid rgba(127, 255, 255, 0.75);
  border: 0px;
  padding: 5px 10px;
  cursor: pointer;
}

button:hover {
  background-color: rgba(0, 255, 255, 0.5);
}

button:active {
  color: #000000;
  background-color: rgba(0, 255, 255, 0.75);
}
</style>
